package classpath

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sail/general"
	"strings"
)

var IsFile = fmt.Errorf("path is to a file, not folder")

type Entry interface {
	classExists(path string) (bool, error)
	findClassFile(path string) (string, error)
	listDir(path string) ([]string, error)
	readClass(path string) ([]byte, Entry, error)

	String() string
}

type DirEntry struct {
	absPath string
}

func newDirEntry(path string) *DirEntry {
	de := new(DirEntry)
	absPath, err := filepath.Abs(path)

	if err != nil {
		panic(err)
	}

	de.absPath = absPath

	return de
}

func (entry *DirEntry) findClassFile(path string) (string, error) {
	p := filepath.Join(entry.absPath, path)

	_, err := os.Stat(p)
	if err != nil {
		if err == os.ErrNotExist {
			return "", err
		}
		return "", err
	}

	return p, nil
}

func (entry *DirEntry) listDir(path string) ([]string, error) {
	dirPath := filepath.Join(entry.absPath, path)

	var targetsFileNames []string

	stat, err := os.Stat(dirPath)

	if err != nil {
		return targetsFileNames, err
	}

	if !stat.IsDir() {
		return nil, IsFile
	}

	err = filepath.Walk(dirPath, func(fileName string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() && fileName != dirPath { // except root
			return filepath.SkipDir // ignore
		}

		if strings.HasSuffix(fileName, general.SailSourceExt) ||
			strings.HasSuffix(fileName, general.SailClassExt) {

			targetsFileNames = append(targetsFileNames, fileName)
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	return targetsFileNames, nil
}

func (entry *DirEntry) classExists(path string) (bool, error) {
	p := filepath.Join(entry.absPath, path)

	stat, err := os.Stat(p)
	if os.IsNotExist(err) || stat.IsDir() {
		return false, nil
	} else if err != nil {
		return false, err
	}

	return true, nil
}

func (entry *DirEntry) readClass(path string) ([]byte, Entry, error) {
	p := filepath.Join(entry.absPath, path)

	data, err := ioutil.ReadFile(p)

	return data, entry, err
}

func (entry *DirEntry) String() string {
	return fmt.Sprintf("[DirEntry]: %v", entry.absPath)
}

func newEntry(path string) Entry {
	return newDirEntry(path)
}
